class iGamePawn extends SimplePawn
	classgroup(iGame)
	hidecategories(TeamBeacon, Swimming, UDKPawn, Movement, Debug, Display, Attachment, Advanced, Object)
	nativereplication;

//=============================================================================
// Basic
//=============================================================================
/** Weapon and items which will be given to unit at spawn time */
var(Inventory) protected array<iGameInventory>			InventoryItems;
/** Default physics state */
var(Physics) EPhysics                                   DefaultPhysics;
/** Default AI controller class to control this unit */
var(AI) const public class<AIController>	            DefaultAiClass;
/** Default AI controller class to control this unit */
var(Inventory) const private EWeapon_Types	            DefaultWeaponType;

//=============================================================================
// Sounds
//=============================================================================
/** Sound played when unit is dying, for example - scream before die */
var(Sounds) const SoundCue  DyingSound;
/** Sound played when unit falling from sky to ground and kick of ground (maybe when flying unit is killed) */
var(Sounds) const SoundCue  FallingDamageLandSound;
/** Voice sound when unit get bullet. Maybe some pain scream */
var(Sounds) const SoundCue  HitSound;
/** VSound played when unit was exploded. This is body explode sound for pawn and metal sound  for vehicle */
var(Sounds) const SoundCue  BodyExplosionSound;
var(Sounds) const int       MaxRadiusWhereSoundCanBeHeard;

//=============================================================================
// Experience
//=============================================================================
/** Amount of experience should be given to killer  */
var(AI) const protected int	ExpirienceForKill;

var int                     Experience;             // Soldier experience, used by player pawn
var int						Level;                  // Level

/** Modifying percent for every level */
const ModifierPercent = 4.55; // % for level modifier

//=============================================================================
// Effects
//=============================================================================
//var protected ParticleSystemComponent			LastSpawnedEffect;

var(Effects) const LinearColor                  BloodColor;

//=============================================================================
// Functions
//=============================================================================
event TickSpecial( float DeltaTime ); //not used
function JumpOffPawn();

simulated function PostBeginPlay()
{
	Super.PostBeginPlay();
	AddDefaultInventory();
}

//=============================================================================
// Effects
//=============================================================================
simulated protected function ParticleSystemComponent ShowEffect(ParticleSystem aTemplate)
{
	local ParticleSystemComponent lLastSpawnedEffect;

	if ( aTemplate != none )
	{
		if ( (WorldInfo != none) && (WorldInfo.NetMode == NM_Standalone) )
		{
			lLastSpawnedEffect = WorldInfo.MyEmitterPool.SpawnEmitter(aTemplate, Location, rot(0,0,0), self);
		}
		else
		{
			lLastSpawnedEffect = new(Outer)class'ParticleSystemComponent';
			lLastSpawnedEffect.SetRotation(rot(0,0,0));
			lLastSpawnedEffect.SetTemplate(aTemplate);
			AttachComponent(lLastSpawnedEffect);
			lLastSpawnedEffect.ActivateSystem();
		}
	}

	return lLastSpawnedEffect;
}

//=============================================================================
// Dying
//=============================================================================
simulated function PlayDying(class<DamageType> aDamageType, vector HitLoc)
{
	local vector ApplyImpulse, ShotDir;
	local TraceHitInfo HitInfo;

	bCanTeleport = false;
	bReplicateMovement = false;
	bTearOff = true;
	bPlayedDeath = true;

	HitDamageType = aDamageType; // these are replicated to other clients
	TakeHitLocation = HitLoc;

	CheckHitInfo( HitInfo, Mesh, Normal(TearOffMomentum), TakeHitLocation );

	// if we had some other rigid body thing going on, cancel it
	if (Physics == PHYS_RigidBody)
	{
		//@note: Falling instead of None so Velocity/Acceleration don't get cleared
		setPhysics(PHYS_Falling);
	}

	PreRagdollCollisionComponent = CollisionComponent;
	CollisionComponent = Mesh;

	Mesh.MinDistFactorForKinematicUpdate = 0.f;

	// If we had stopped updating kinematic bodies on this character due to distance from camera, force an update of bones now.
	if( Mesh.bNotUpdatingKinematicDueToDistance )
	{
		Mesh.ForceSkelUpdate();
		Mesh.UpdateRBBonesFromSpaceBases(TRUE, TRUE);
	}

	Mesh.PhysicsWeight = 1.0;

	SetPhysics(PHYS_RigidBody);
	Mesh.PhysicsAssetInstance.SetAllBodiesFixed(FALSE);
	SetPawnRBChannels(TRUE);

	if( TearOffMomentum != vect(0,0,0) )
	{
		ShotDir = normal(TearOffMomentum);
		ApplyImpulse = ShotDir * aDamageType.default.KDamageImpulse;

		// If not moving downwards - give extra upward kick
		if ( Velocity.Z > -10 )
		{
			ApplyImpulse += Vect(0,0,1)*aDamageType.default.KDeathUpKick;
		}
		Mesh.AddImpulse(ApplyImpulse, TakeHitLocation, HitInfo.BoneName, true);
	}
	GotoState('Dying');
}

function bool Died(Controller Killer, class<DamageType> DamageType, vector HitLocation)
{
	iGamePawn(Killer.Pawn).UpdateExperience(Self.ExpirienceForKill);
	return super.Died(Killer, DamageType, HitLocation);
}

simulated function SetPawnRBChannels(bool bRagdollMode)
{
	if(bRagdollMode)
	{
		Mesh.SetRBChannel(RBCC_Pawn);
		Mesh.SetRBCollidesWithChannel(RBCC_Default,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_Pawn,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_Vehicle,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_Untitled3,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_BlockingVolume,TRUE);
	}
	else
	{
		Mesh.SetRBChannel(RBCC_Untitled3);
		Mesh.SetRBCollidesWithChannel(RBCC_Default,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_Pawn,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_Vehicle,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_Untitled3,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_BlockingVolume,FALSE);
	}
}

State Dying
{
	ignores OnAnimEnd, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer;

	simulated function BeginState(Name PreviousStateName)
	{
		Super.BeginState(PreviousStateName);
		
		PlayDyingSound();

		CylinderComponent.SetActorCollision(false, false);

		if ( Mesh != None )
		{
			Mesh.SetTraceBlocking(true, true);
			Mesh.SetActorCollision(true, false);
			Mesh.SetTickGroup(TG_PostAsyncWork); // Move into post so that we are hitting physics from last frame, rather than animated from this
		}

		LifeSpan = 15;
	}

	//simulated event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> aDamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
	//{
	//	local Vector shotDir, ApplyImpulse,BloodMomentum;
	//	local Emitter HitEffect;

	//	if (InstigatedBy != None || EffectIsRelevant(Location, true, 0))
	//	{
	//		Health -= Damage;

	//		if (WorldInfo.NetMode != NM_DedicatedServer)
	//		{
	//			CheckHitInfo( HitInfo, Mesh, Normal(Momentum), HitLocation );
	//			if ( lDamage != None )
	//			{
	//				lDamage.Static.SpawnHitEffect(self, Damage, Momentum, HitInfo.BoneName, HitLocation);
	//			}
	//			if (aDamageType.default.bCausesBlood && ((PlayerController(Controller) == None) || (WorldInfo.NetMode != NM_Standalone)) )
	//			{
	//				BloodMomentum = Momentum;
	//				if ( BloodMomentum.Z > 0 ) BloodMomentum.Z *= 0.5;
	//				HitEffect = Spawn(BloodEmitterClass, self, , HitLocation, rotator(BloodMomentum));
	//				X_COM_Emitter(HitEffect).AttachTo(Self, HitInfo.BoneName);
	//			}

	//			if( (Physics != PHYS_RigidBody) || (Momentum == vect(0,0,0)) || (HitInfo.BoneName == '') )
	//				return;

	//			shotDir = Normal(Momentum);
	//			ApplyImpulse = (aDamageType.Default.KDamageImpulse * shotDir);

	//			if( (lDamage != None) && lDamage.Default.bThrowRagdoll && (Velocity.Z > -10) )
	//			{
	//				ApplyImpulse += Vect(0,0,1)*aDamageType.default.KDeathUpKick;
	//			}
	//			// AddImpulse() will only wake up the body for the bone we hit, so force the others to wake up
	//			Mesh.WakeRigidBody();
	//			Mesh.AddImpulse(ApplyImpulse, HitLocation, HitInfo.BoneName, true);
	//		}
	//	}
	//}
}

//=============================================================================
// Experience
//=============================================================================
simulated public function UpdateExperience(optional int aExpQuantity)
{
	if (aExpQuantity != 0) GiveExperience(aExpQuantity);
}

simulated public function GiveExperience(int aExpQuantity)
{
	Experience += aExpQuantity;
	CheckLevelIncreased();
}

private function CheckLevelIncreased()
{
	local int lExpForNextLevel;
	local int lNextLevel;
	
	lNextLevel = Level + 1;

	lExpForNextLevel = (lNextLevel * ModifierPercent * abs(1-lNextLevel)) * 100;

	if (Experience >= lExpForNextLevel) IncreaseLevel();
}

simulated private function IncreaseLevel()
{
	Level++;
}

//=============================================================================
// Functions Inventory
//=============================================================================
/** Create an inventory item from Archetype rather than direct class reference */
simulated public final function Inventory CreateInventoryFromTemplate( Inventory InventoryActorTemplate, optional bool bDoNotActivate )
{
	if ( InvManager != None )
		return iGameInventoryManager(InvManager).CreateInventoryFromTemplate( InventoryActorTemplate, bDoNotActivate );

	return None;
}

/** Adds InventoryItems from archetype */
simulated function AddDefaultInventory()
{
	local int il;

	if (InventoryItems.Length >= 0)
	{
		for(il=0; il<InventoryItems.Length; ++il)
		{
			if (InventoryItems[il] != none) CreateInventoryFromTemplate(InventoryItems[il], true);
		}
	}

	AddDefaultWeapon();
}

simulated function AddDefaultWeapon()
{
	CreateInventoryFromTemplate(iGameInfo(worldinfo.game).Weapon_Types[DefaultWeaponType]);
}

simulated singular event Rotator GetBaseAimRotation()
{
   local rotator   POVRot, tempRot;
   tempRot = Rotation;
   tempRot.Pitch = 0;
   tempRot.Roll = 0;
   SetRotation(tempRot);
   POVRot = Rotation;
   POVRot.Pitch = 0;
   POVRot.Roll = 0;
   return POVRot;
}

//=============================================================================
// Controller
//=============================================================================
public function ChangeController(class<controller> aClass)
{
	local controller lcontroller;

	if (controller != none)
	{
		lcontroller = controller;
		lcontroller.UnPossess();
		lcontroller.Destroy();
	}

	if(aClass != none)
	{
		Controller = Spawn(aClass,,,Location,Rotation,,false);
		Controller.Possess(self, false);
		SetOwner(Controller);
	}
}

function PossessedBy(Controller C, bool bVehicleTransition)
{
	super.PossessedBy(C, bVehicleTransition);
	SetPhysics(DefaultPhysics);
}

//=============================================================================
// default properties
//=============================================================================
defaultproperties
{
	Begin Object Class=DynamicLightEnvironmentComponent Name=PawnLightEnvironment
         AmbientGlow=(R=0.75,G=0.75,B=0.75,A=1.0)
         AmbientShadowColor=(R=0.0,G=0.0,B=0.0)
         bSynthesizeSHLight=FALSE
         LightDistance=128
         ShadowDistance=0
         ModShadowFadeoutExponent=1
         bCastShadows=FALSE
		 MinTimeBetweenFullUpdates = 0.3
		 bCompositeShadowsFromDynamicLights=TRUE
         bDynamic=TRUE
         MinShadowAngle=0
    End Object
    Components.Add(PawnLightEnvironment)

    Begin Object Class=SkeletalMeshComponent Name=PawnSkeletalMesh
		AlwaysLoadOnClient=TRUE
		AlwaysLoadOnServer=TRUE	
		BlockRigidBody=TRUE
		BlockActors=TRUE
		BlockZeroExtent=TRUE
		BlockNonZeroExtent=TRUE
		bAllowApproximateOcclusion=TRUE
		bForceDirectLightMap=TRUE	
		bCacheAnimSequenceNodes=FALSE
		bIgnoreControllersWhenNotRendered=FALSE
		bUpdateKinematicBonesFromAnimation=TRUE
		bCastDynamicShadow=TRUE
		bOwnerNoSee=FALSE	
		bChartDistanceFactor=TRUE
		bOverrideAttachmentOwnerVisibility=TRUE
		bAcceptsDecals=FALSE
		bAcceptsDynamicDecals=FALSE
		bHasPhysicsAssetInstance=TRUE
		bEnableFullAnimWeightBodies=TRUE
		bUpdateSkelWhenNotRendered=TRUE
		bUseAsOccluder=FALSE
		bUsePrecomputedShadows=FALSE
		CastShadow=TRUE
		CollideActors=TRUE
		LightEnvironment=PawnLightEnvironment
		LightingChannels=(Dynamic=TRUE,CompositeDynamic=TRUE)
		MinDistFactorForKinematicUpdate=0.2
		Translation=(X=0.0000000,Y=0.00000000,Z=0)		
		TickGroup=TG_PreAsyncWork
		RBChannel=RBCC_Pawn
		RBCollideWithChannels=(Default=TRUE,BlockingVolume=TRUE,GameplayPhysics=TRUE,EffectPhysics=TRUE,Vehicle=TRUE,Untitled3=TRUE, Pawn=TRUE)
	End object
	Mesh=PawnSkeletalMesh;
	Components.Add(PawnSkeletalMesh);

	Begin Object Name=CollisionCylinder
		CollisionRadius=+0025.000000
		CollisionHeight=+0050.000000
		BlockZeroExtent=FALSE
	End Object
	CylinderComponent=CollisionCylinder
	CollisionComponent=CollisionCylinder
	CollisionType=COLLIDE_BlockAll

	bRunPhysicsWithNoController = TRUE
	bNoEncroachCheck = TRUE
	bCollideWorld = TRUE

	Role = ROLE_Authority
	RemoteRole = ROLE_SimulatedProxy
	bAlwaysRelevant = TRUE
	AlwaysRelevantDistanceSquared=+1960000.0
	bReplicateHealthToAll = TRUE

	GroundSpeed=600.0
	JumpZ = 0;
	RotationRate=(Pitch=0,Yaw=16384,Roll=0)

	SightRadius=512
	PeripheralVision = 0.707; // 90 degree
	HearingThreshold = 512

	InventoryManagerClass=class'iGameInventoryManager'
}